package com.xquant.pricing.calc.controller;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson2.JSON;
import com.github.pagehelper.PageInfo;
import com.xquant.common.core.constant.DictConstant;
import com.xquant.common.engine.xPP.cxf.XQuantPPStub;
import com.xquant.common.engine.xPP.cxf.webservice.*;
import com.xquant.common.engine.xPP.request.*;
import com.xquant.common.engine.xPP.response.CalcMarket;
import com.xquant.common.engine.xPP.response.CalcResult;
import com.xquant.common.engine.xPP.response.Contract;
import com.xquant.common.log.annotation.Log;
import com.xquant.common.log.enums.BusinessType;
import com.xquant.common.security.utils.SecurityUtils;
import com.xquant.pricing.calc.entity.ProductTypeEnum;
import com.xquant.pricing.calc.entity.RequestCodeEnum;
import com.xquant.pricing.calc.entity.ResponseVo;
import com.xquant.pricing.calc.service.impl.otcOption.CalcCommonService4OtcOptionSplitService;
import com.xquant.pricing.calc.service.impl.otcOption.CalcMarketServiceImpl4OtcOption;
import com.xquant.pricing.common.utils.StringUtils;
import com.xquant.pricing.pricingConfig.service.EngineConfigService;
import com.xquant.pricing.pricingConfig.service.OptionContractService;
import com.xquant.pricing.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.*;

/**
 * newCloud
 * 功能说明：计算服务场外期权
 * 编写作者：xeq
 * 开发日期：
 * 修改记录：修改日期   修改人    修改内容
 */
@RestController
@RequestMapping("otcOption")
public class CalcOtcOptionController extends CalcBaseController {

    @Autowired
    private EngineConfigService engineConfigService;

    @Autowired
    CalcCommonService4OtcOptionSplitService calcCommonService4OtcOptionSplitService;

    @Autowired
    CalcMarketServiceImpl4OtcOption calcMarketServiceImpl4OtcOption;

    @Override
    public String getProductType() {
        return ProductTypeEnum.OTC_OPTION.getCode();
    }

    @Autowired
    private OptionContractService optionContractService;
    public ResponseVo handleBlock(@RequestBody CalcEngineParam requestVo, BlockException e) {
        ResponseVo result = new ResponseVo();
        result.setErrorCode(-1);
        result.setErrorMsg("Sentinel漏斗算法流量控制了！");
        System.out.println("Sentinel漏斗算法流量请求频繁，控制了！");
        return result;
    }
    // 降级处理方法
    public String handleFallback() {
        System.out.println("handleFallback");
        return "Degrade response";
    }
    /**
     * 期权 市场计算
     */
    @RequestMapping(value = "/calcMarket.action")
    @SentinelResource(value = "/pricing/otcOption/calcMarket", blockHandler = "handleBlock", fallback = "handleFallback")
    @Log(title = "期权试定价", businessType = BusinessType.CALC)
    @Override
    public ResponseVo calcMarket(@RequestBody CalcEngineParam param) {
        /*// 业务系统请求计算
        ContextHolder.getParam().put("userId", SecurityUtils.getUserId());
        ContextHolder.getParam().put("logType", param.getLogType());

        ContextHolder.getParam().put("operateDate", DateUtils.getCurr_YYYY_MM_DD());
        ContextHolder.getParam().put("calcType", RequestCodeEnum.CALC_MARKET.getCode());
        ContextHolder.getParam().put("calcDate", DateUtils.getCurr_YYYY_MM_DD());
        ContextHolder.getParam().put("productType", getProductType());
        ContextHolder.getParam().put("pForm", JSONUtil.toJsonStr(param));
        ContextHolder.getParam().put("pFormInstrument", JSONUtil.toJsonStr(param.getCalcInstrumentForm()));
        ContextHolder.getParam().put("pFormPricing", JSONUtil.toJsonStr(param.getCalcPricingForm()));
        if (param.getSplit()) {
            param = calcCommonService4OtcOptionSplitService.beforeCalc(param);
        }
        return doCalcMarket(param);*/
        CalcResult calcResult = new CalcResult();
        Contract contract = new Contract();
        contract.setNpv(RandomUtil.randomNumbers(5));
        contract.setDelta(RandomUtil.randomNumbers(5));
        contract.setTheta(RandomUtil.randomNumbers(5));
        contract.setGamma(RandomUtil.randomNumbers(5));
        contract.setVega(RandomUtil.randomNumbers(5));
        contract.setRho(RandomUtil.randomNumbers(5));
        CalcMarket calcMarket = new CalcMarket();
        calcResult.setCalcMarket(calcMarket);
        calcResult.getCalcMarket().setContract(contract);
        String result = JSON.toJSONString(calcResult);
        return ResponseVo.success(result);
    }

    /**
     * 反算波动率
     */
    @RequestMapping(value = "/calcMarketVol.action")
    public Object calcMarketVol(@RequestBody CalcEngineParam param) {
        // 业务系统请求计算
        ContextHolder.getParam().put("uId", SecurityUtils.getUserId());
        ContextHolder.getParam().put("calcType", RequestCodeEnum.CALC_MARKET.getCode());
        ContextHolder.getParam().put("calcDate", DateUtils.getCurr_YYYY_MM_DD());
        ContextHolder.getParam().put("productType", getProductType());
//        param = calcCommonService4OtcOptionSplitService.beforeCalc(param);
        return calcMarketVolResult(param);

    }

    @ResponseBody
    @RequestMapping(value = "/queryEngineCfg4Page.action")
    public Object queryEngineCfg4Page(@RequestParam Map<String, Object> params) {
        PageInfo<Map<String, Object>> pageInfo = engineConfigService.queryEngineConfigByPage(params);
        return pageInfo;
    }

    @ResponseBody
    @RequestMapping(value = "/queryExcelTemplate.action")
    public Object queryExcelTemplate() {
        URL resource = CalcOtcOptionController.class.getClassLoader().getResource("templates/excel/");
        if (resource == null) return null;
        File f = new File(resource.getPath() + File.separator);
        // 排除目录，只要文件
        File[] fileArray = f.listFiles(pathname -> !pathname.isDirectory());
        // 转成集合，便于排序
        if (fileArray == null) return null;
        List<Map<String, Object>> fileList = new ArrayList<>();
        HashMap<String, Object> fileInfo;
        for (File file : Objects.requireNonNull(fileArray)) {
            fileInfo = new HashMap<>();
            fileInfo.put("fileName", file.getName());
            fileInfo.put("fileSize", file.length());
            fileInfo.put("updateDate", new Date(file.lastModified()));
            fileList.add(fileInfo);
        }
        return fileList;
    }

    /**
     * combobox使用
     *
     * @param params
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/queryEngineCfg.action")
    public Object queryEngineCfg(@RequestParam Map<String, Object> params) {
        try {
            PageInfo<Map<String, Object>> pageInfo = engineConfigService.queryEngineConfigByPage(params);
            return ResponseVo.success(pageInfo.getList());
        } catch (Exception e) {
            return ResponseVo.failure(e.getMessage());
        }
    }

    /**
     * 对外接口，combobox使用
     *
     * @param params
     * @return
     */
    @ResponseBody
    @RequestMapping(value = "/queryIeEngineCfg.action")
    public Object queryIeEngineCfg(@RequestBody Map<String, Object> params) {
        try {
            PageInfo<Map<String, Object>> pageInfo = engineConfigService.queryEngineConfigByPage(params);
            return ResponseVo.success(pageInfo.getList());
        } catch (Exception e) {
            return ResponseVo.failure(e.getMessage());
        }
    }

    @Override
    protected ResponseVo doCalcMarket(CalcEngineParam param) {
        /*针对雪球期权做自定义参数的处理*/
        if (null == param.getCalcInstrumentForm()) {
            return super.doCalcMarket(param);
        }
        //20220617 botao.yu 增加试定价价格校验
        String productType = param.getCalcInstrumentForm().getProductType();
        String customPricingStr = "";
        if (productType.startsWith("ELN_ACB")) {
            LinkedHashMap jsonObject = (LinkedHashMap) param.getCustomPricingParam();
            String type = String.valueOf(jsonObject.get("type"));//类型
            String routes = String.valueOf(jsonObject.get("routes"));//路劲数
            switch (type) {
                case "QD":
                    customPricingStr = "<root>\n" +
                            "       <Parameter>\n" +
                            "              <KeyValues>\n" +
                            "                     <GridSize DATATYPE=\"INT\" TYPE=\"SIMPLE\">" +
                            routes +
                            "</GridSize>\n" +
                            "                     <RangeScale DATATYPE=\"INT\" TYPE=\"SIMPLE\">10</RangeScale>\n" +
                            "              </KeyValues>\n" +
                            "       </Parameter>\n" +
                            "</root>";
                    break;
                case "MC":
                    customPricingStr = "<?xml version=\"1.0\" encoding=\"GB2312\"?><root><Parameter><KeyValues><MCPathNum DATATYPE = \"INT\" TYPE = \"SIMPLE\" >" +
                            routes +
                            "</MCPathNum><VarianceReductionMethod DATATYPE = \"INT\" TYPE = \"SIMPLE\" >0</VarianceReductionMethod></KeyValues></Parameter></root>";
                    break;
                case "BS":

                    break;
            }
            param.setCustomPricingParam(customPricingStr);
        }
        return super.doCalcMarket(param);
    }

    @Override
    protected ResponseVo calcMarketVolResult(CalcEngineParam param) {

        return super.calcMarketVolResult(param);
    }


    @RequestMapping(value = "/calcMaturityPayment/getCalcPricingParamByCfgId.action")
    public ResponseVo getCalcPricingParamByCfgId4CalcMaturityPayment(@RequestBody CalcEngineParam param) {
        CalcEngineParam engineParam = getCalcPricingParamByCfgId(param, RequestCodeEnum.CALC_MATURITY_PAYMENT.getCode());
        return ResponseVo.success(engineParam);
    }

    @RequestMapping(value = "/calcMaturityPayment.action")
    public Object calcMaturityPayment(@RequestBody CalcEngineParam param) {
        return doNormalCalcEngine(param, RequestCodeEnum.CALC_MATURITY_PAYMENT.getCode());
    }

    /**
     * 反算合约要素
     *
     * @param params
     * @return
     */
    @RequestMapping("/calcContractParam.action")
    public ResponseVo doCalcEquityOptionImplContract(@RequestBody CalcEngineParam params) {

        EquityOptionNPVParam param = new EquityOptionNPVParam();
        DictConstant.OPTION_TYPE enumType = DictConstant.OPTION_TYPE.getEnumType(params.getInstrument().getProductType());
        switch (enumType) {
            case RANGEACCRUAL:
                param.setInstrumentID(DateUtils.getCurr_YYYYMMDDHHMMSSSSS() + ",STU_IDX_S,NONE");
                break;
            case MULTILAYERDIGITAL:
            case SHARK:
                param.setInstrumentID(DateUtils.getCurr_YYYYMMDDHHMMSSSSS() + ",OPT_CMDT,OTHER");
                break;
            case VANILLA:
            case DIGITAL:
                param.setInstrumentID(DateUtils.getCurr_YYYYMMDDHHMMSSSSS() + ",OPT_S,NONE");
                break;
            default:
                return ResponseVo.failure("暂未支持" + DictConstant.OPTION_TYPE.getEnumType(params.getInstrument().getProductType()) + "的反算合约要素");
        }

        {
            PersonalInsParam insParam = new PersonalInsParam();
            try {
                insParam.setFpML(Base64.getEncoder().encodeToString(params.getInstrument().getTermsheet().toString().getBytes("GBK")));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
            insParam.setTermSheetType("1");
            param.setInsParam(insParam);
        }
        CalcPricingParam calcPricingParam = params.getCalcPricingParam();
        param.setValueDate(calcPricingParam.getValueDate());
        param.setCalcPPCode(calcPricingParam.getCalcPPCode());
        // 贴现
        Ir discount = calcPricingParam.getDiscount();
        if (StringUtils.isEmpty(discount.getCode())) {
            param.setDscRate(Double.parseDouble(discount.getRate()));
            param.setDayCounterD(discount.getDayCount());
            param.setDscRateType(discount.getType());
        } else {
            try {
                param.setDscCurveCode(Base64.getEncoder().encodeToString(discount.getCode().getBytes("GBK")));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }

        // 融资
        UnderlyingPricingParam underlyingPricingParam = calcPricingParam.getUnderlyingPricingParams().get(0);

        param.setSpotPrice(Double.parseDouble(underlyingPricingParam.getSpotPrice()));
        Ir funding = underlyingPricingParam.getFunding();
        if (StringUtils.isEmpty(funding.getCode())) {
            param.setFundingRate(Double.parseDouble(funding.getRate()));
            param.setDayCounterF(funding.getDayCount());
            param.setFundingRateType(funding.getType());
        } else {
            try {
                param.setFundingCurveCode(Base64.getEncoder().encodeToString(funding.getCode().getBytes("GBK")));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }

        // 分红
        Ir dividend = underlyingPricingParam.getDividend();
        if (StringUtils.isEmpty(dividend.getCode())) {
            param.setDivRate(Double.parseDouble(dividend.getRate()));
            param.setDayCounterDiv(dividend.getDayCount());
            param.setDivRateType(dividend.getType());
        } else {
            try {
                param.setDivCurveCode(Base64.getEncoder().encodeToString(dividend.getCode().getBytes("GBK")));

            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }

        // 波动率
        Ir volSurface = underlyingPricingParam.getVolSurface();
        if (StringUtils.isEmpty(volSurface.getCode())) {
            param.setVolRate(Double.parseDouble(volSurface.getRate()));
            param.setDayCounterV(volSurface.getDayCount());
        } else {
            try {
                param.setVolSurfaceCode(Base64.getEncoder().encodeToString(volSurface.getCode().getBytes("GBK")));
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        param.setIsRecalculate("1");
        param.setVolAnnualDays(volSurface.getAnnualDays());

        EquityOptionImplContractParam contractParam = new EquityOptionImplContractParam();

        contractParam.setRoots(params.getContractRoots());

        // 设置期权结构
        contractParam.setNPVParam(param);
        //设置Npv
        contractParam.setMarketPrice(calcPricingParam.getMarketNPV());
        /*设置最优区间*/
        contractParam.setSolverParam(params.getSectionSolvers());

        XQuantPPStub stub = null;
        try {
            stub = ppService.borrowStubServer();
            CalcEquityOptionImplContractResponse calcEquityOptionImplContractResponse = stub.calcEquityOptionImplContract(contractParam);
            logger.info(JSON.toJSONString(calcEquityOptionImplContractResponse.getResult()));
            int errorCode = calcEquityOptionImplContractResponse.getCalcEquityOptionImplContractResult().getErrorCode();
            if (errorCode < 0) {
                return ResponseVo.failure(calcEquityOptionImplContractResponse.getCalcEquityOptionImplContractResult().getErrorMsg());
            }
            return ResponseVo.success(calcEquityOptionImplContractResponse.getResult());
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (stub != null) {
                ppService.returnStub(stub);
            }
        }
    }

    /**
     * @param query 获取 波动率曲面插值
     *              计算日 valueDate
     *              到期日 InterpolationDate
     *              曲面代码 code
     *              定盘价格 spot
     *              行权价 strike
     * @return
     */
    @RequestMapping(value = "/calcMarket/calcVolSurfaceRateBase64.action")
    public ResponseVo calcVolSurfaceRateBase64(@RequestBody Map<String, Object> query) throws UnsupportedEncodingException {
        CalcVolSurfaceRateBase64Response calcVolSurfaceRateBase64Response = calcMarketServiceImpl4OtcOption.calcVolSurfaceRateBase64(query);
        HashMap hashMap = optionContractService.queryTvolsurfaceDaycounter(query);
        int errorCode = calcVolSurfaceRateBase64Response.getCalcVolSurfaceRateBase64Result().getErrorCode();
        if (errorCode < 0) {
            return ResponseVo.failure(calcVolSurfaceRateBase64Response.getCalcVolSurfaceRateBase64Result().getErrorMsg());
        }
        hashMap.put("result", calcVolSurfaceRateBase64Response.getResult());
        return ResponseVo.success(hashMap);
    }

    /**
     * 获取 分红曲线插值
     * 计算日 valueDate
     * 曲线代码  code
     * 到期日 PeriodsOrDates
     *
     * @param query
     * @return
     */
    @RequestMapping(value = "/calcMarket/calcDivCurveRateBase64.action")
    public ResponseVo CalcDivCurveRateBase64(@RequestBody Map<String, Object> query) throws UnsupportedEncodingException {
        HashMap hashMap = new HashMap();
        hashMap.put("result", "0.3");
        return ResponseVo.success(hashMap);
    }


    /**
     * 获取 贴现曲线插值
     * 计算日  valueDate
     * 到期日  PeriodsOrDates
     * 曲线代码 code
     *
     * @param query
     * @return
     */
    @RequestMapping(value = "/calcMarket/calcCurveRateBase64.action")
    public ResponseVo calcCurveRateBase64(@RequestBody Map<String, Object> query) throws UnsupportedEncodingException {

        CalcCurveRatesBase64Response calcCurveRatesBase64Response = calcMarketServiceImpl4OtcOption.calcCurveRateBase64(query);

        int errorCode = calcCurveRatesBase64Response.getCalcCurveRatesBase64Result().getErrorCode();
        if (errorCode < 0) {
            return ResponseVo.failure(calcCurveRatesBase64Response.getCalcCurveRatesBase64Result().getErrorMsg());
        }
        HashMap hashMap = optionContractService.queryTircurveDaycounter(query);
        hashMap.put("result", calcCurveRatesBase64Response.getResult());
        return ResponseVo.success(hashMap);
    }
}
